package streaming;

import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;

import simulation.Config;
import simulation.FileLog;
import simulation.FullStatistics;
import simulation.Logs;
import simulation.PeriodicStats;
import simulation.Simulator;
import simulation.Statistics;
import simulation.TrackerRequest;

public class Chainsaw {

	Overlay overlay;
	PeriodicStats periodicStats = new PeriodicStats();
	String filename;
	
	public Chainsaw() {
	}

	public static void main(String[] args) {
		int maliciousPeersRatio = 1; // ratio of malicious peers (1(0%), 2(5%), 3(10%), 4(15%), 5(20%), 6(25%))
		int endMaliciousPeersRatio = 6; // end of ratio. Used to finish the first for loop below
		boolean ratioMaliciousPeers = false;
//		Integer param_numberOfPeers = new Integer(0);
		
		if (args.length != 0){
			int numArgs = 0;
			while ( numArgs < args.length ){
				// Argument '-i' -> initial simulation counter to be written in files
				if (args[numArgs].equalsIgnoreCase("-i")){ 
					
					if (numArgs+1 < args.length){ // Check if can access the next position, i.e., if it is not null
						try {
							Config.START_COUNTER_SIMULATIONS = Integer.parseInt(args[numArgs+1]);
							Config.END_COUNTER_SIMULATIONS = Config.START_COUNTER_SIMULATIONS + Config.NUMBER_OF_SIMULATIONS;
							numArgs++; // step one argument (the value of the argument)
						} 
						catch (NumberFormatException nfe) {
							System.out.println("Error: Value of argument -i must be a number.");
						}
					}
					else {
						System.out.println("Error: Argument -i needs a value.");
						System.exit(1);
					}
				}
				// If the ratio of malicious peers were set...
				if (args[numArgs].equalsIgnoreCase("-m")){ 
					ratioMaliciousPeers = true;
					
					if (numArgs+1 < args.length){ // Check if can access the next position, i.e., if it is not null
						try {
							maliciousPeersRatio = new Integer(args[numArgs+1]);
							endMaliciousPeersRatio = maliciousPeersRatio;
							numArgs++; // step one argument (the value of the argument)
						} 
						catch (NumberFormatException nfe) {
							System.out.println("Error: Value of argument -m must be a number.");
						}
					}
					else {
						System.out.println("Error: Argument -m needs a value.");
						System.exit(1);
					}
				}
				
				numArgs++;
			}
		}

		if (!ratioMaliciousPeers) {
			System.out.println("Simulando sem parâmetro de peers maliciosos.");
		}
		else {
			System.out.println("Simulando com parâmetro de peers maliciosos = " + maliciousPeersRatio);
		}
		/*
		for (int numSim = Config.START_COUNTER_SIMULATIONS; numSim < Config.END_COUNTER_SIMULATIONS ; numSim++){
		
			// These fors are to set the simulation parameters
			for (; maliciousPeersRatio <= endMaliciousPeersRatio; maliciousPeersRatio++){
				switch (maliciousPeersRatio){
				case 1: // no malicious peers;
					StreamingConfig.SIMULATION_WITH_ATTACKERS = false;
					StreamingConfig.ATTACKERS_RATIO = 0.0;
				break;
				case 2: // 5% of malicious peers;
					StreamingConfig.ATTACKERS_RATIO = 0.05;
				break;	
				case 3: // 10% of malicious peers;
					StreamingConfig.ATTACKERS_RATIO = 0.1;
				break;
				case 4: // 15% of malicious peers;
					StreamingConfig.ATTACKERS_RATIO = 0.15;
				break;
				case 5: // 20% of malicious peers;
					StreamingConfig.ATTACKERS_RATIO = 0.20;
				break;
				case 6: // 25% of malicious peers;
					StreamingConfig.ATTACKERS_RATIO = 0.25;
				break;
				}
				
				// Number of peers
				for (int numberOfPeers = 1 ; numberOfPeers <= 3 ; numberOfPeers++){
					switch (numberOfPeers){
					case 1: // 200 initial peers
						StreamingConfig.INITIAL_HOSTS_NUMBER = 200;
					break;	
					case 2: // 500 initial peers
						StreamingConfig.INITIAL_HOSTS_NUMBER = 500;
					break;
					case 3: // 1000 initial peers
						StreamingConfig.INITIAL_HOSTS_NUMBER = 1000;
					break;
					}
					
	//				if ( param_numberOfPeers > 0 && param_numberOfPeers != StreamingConfig.INITIAL_HOSTS_NUMBER ) {
	//					continue;
	//				}
				
					// Type of churn to be used
					for (int churnType = 1; churnType <=3 ; churnType++){
						switch (churnType){
						case 1: // without churn
							StreamingConfig.DYNAMIC_INSERTION_ON = false;
							StreamingConfig.DYNAMIC_REMOVAL_ON = false;
						break;	
						case 2: // 50% of churn (half of the peers are removed during the execution)
							StreamingConfig.DYNAMIC_INSERTION_ON = true;
							StreamingConfig.DYNAMIC_REMOVAL_ON = true;
							
							StreamingConfig.HOSTS_NUMBER_TO_INSERT_ON_DISTRIBUTION = (int) (StreamingConfig.INITIAL_HOSTS_NUMBER * 0.5);
							StreamingConfig.HOSTS_NUMBER_TO_REMOVE_ON_DISTRIBUTION = (int) (StreamingConfig.INITIAL_HOSTS_NUMBER * 0.5);
						break;
						case 3: // 100% of churn (100% of the peers are removed during the execution)
							StreamingConfig.DYNAMIC_INSERTION_ON = true;
							StreamingConfig.DYNAMIC_REMOVAL_ON = true;
		
							StreamingConfig.HOSTS_NUMBER_TO_INSERT_ON_DISTRIBUTION = (int) StreamingConfig.INITIAL_HOSTS_NUMBER;
							StreamingConfig.HOSTS_NUMBER_TO_REMOVE_ON_DISTRIBUTION = (int) StreamingConfig.INITIAL_HOSTS_NUMBER;
						break;
						}
	
						// tracker monitorament interval
						for (int monitoramentInterval = 1; monitoramentInterval <= 2; monitoramentInterval++){
							switch (monitoramentInterval){
							case 1: // interval of monitorament = 1
								StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL = 1.0;
							break;	
							case 2: // interval of monitorament = 2
								StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL = 15.0;
							break;
							}
							
							if (maliciousPeersRatio == 1){
								String polluterType = "none";
								
								// Churn used 
								String churnUsed = (churnType == 2) ? "comch50" : (churnType == 3) ? "comch100" : "semch";
								
								System.out.println("["+ StreamingConfig.ATTACKERS_RATIO + "," + polluterType + "," + churnUsed +"," + StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "]"); 
								
								
								startSimulation(numSim);
							}
							else{ // the polluter ratio is greater than 0				
								
								// Set parameters for the type of malicious peer, but ONLY if there are malicious peers (of course!)
								for (int typeMaliciousPeer = 1; typeMaliciousPeer <= 3 ; typeMaliciousPeer++){
									switch (typeMaliciousPeer){
									case 1: // always pollute
										StreamingConfig.ATTACK_TYPE = 5;
										StreamingConfig.ALWAYS_POLLUTE = true;
									break;	
									case 2: // pollutes randomly
										StreamingConfig.ATTACK_TYPE = 5;
										StreamingConfig.ALWAYS_POLLUTE = false;
									break;
									case 3: // omites
										StreamingConfig.ATTACK_TYPE = 3;
									break;
									}
									
									String polluterType;
									
									if (StreamingConfig.ATTACK_TYPE == 3) {
										polluterType = "omite";
									}
									else {
										if (StreamingConfig.ALWAYS_POLLUTE) {
											polluterType = "sempre";
										}
										else {
											polluterType = "aleat";
										}
									}
									String churnUsed = "semch";
									
									// Churn used 
									if (StreamingConfig.DYNAMIC_INSERTION_ON){
										churnUsed = "comch" + 100 * StreamingConfig.HOSTS_NUMBER_TO_INSERT_ON_DISTRIBUTION / StreamingConfig.INITIAL_HOSTS_NUMBER; 
									}
									
									
									System.out.println("["+ StreamingConfig.ATTACKERS_RATIO + "," + polluterType + "," + churnUsed +"," + StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "]");
									
									startSimulation(numSim);
								}
							}
						}
					}
				}
			}
		}*/
		startSimulation(1);
	}
	
	public static void startSimulation(int numSim){
		int simulacao = 0;
		//for (int aux = 1 ; aux <= 1 ; aux++){
		simulacao++;
		FileLog.openFile(simulacao + "");
		//Chainsaw.setConfigurations(aux);
	
		FileLog.getInstance(); // true means to append file
		FileLog.getInstance().appendStringResults("chunks sent, chunks sent by CM, # of requests, # of U sets sent to tracker, polluted peers, mean of polluted peers in diagnosed chunks, % of polluted peers diagnosticated\n");
		
			Chainsaw chainsaw = new Chainsaw();
			chainsaw.overlay = Overlay.getInstance();
			
			Logs.printMessage("main", "-------------------- Início Execução " + (numSim+1) + " --------------------");
			Logs.printMessage("summary", "-------------------- Início Execução " + (numSim+1) + " --------------------");
			
			// checks whether simulation will have or not attackers
			if (StreamingConfig.SIMULATION_WITH_ATTACKERS){
				// Check if the attack type selected does exist
				if (StreamingConfig.ATTACK_TYPE >= 1 && StreamingConfig.ATTACK_TYPE <= 5){
					chainsaw.experimentWithAttackers(StreamingConfig.ATTACK_TYPE, StreamingConfig.ATTACKERS_RATIO);
				}
				else {
					System.out.println("The attack type " + StreamingConfig.ATTACK_TYPE  + " couldn't be found.");
					System.exit(1);
				}
			}
			else {
				chainsaw.experimentWithoutAttackers();
			}
			
			Object[] hostsList = chainsaw.overlay.hosts.values().toArray();
			
			Arrays.sort(hostsList, chainsaw.new idComparator());
			
			int media = 0;
			for (int j = 0; j < hostsList.length; j++){
				Host auxHost = (Host) hostsList[j];
				
				System.out.println("\n********** Host " + auxHost.getId() + " **********");
				Iterator iterator = auxHost.neighbors.keySet().iterator();
				Neighbor neighbor;
				
				if (Config.USE_COMPARATOR_MODULE){
					System.out.print("Vizinhos: [");
					while (iterator.hasNext()) {
						neighbor = (Neighbor)auxHost.neighbors.get(iterator.next());
						System.out.print(neighbor.id + ",");				
					}
					System.out.println("]\n");
//					
//					// Print comparator requests
//					for (RequestedPacket rp : auxHost.comparatorModule.getMapRequestedChunks().values()){
//						System.out.print("Chunk " + rp.getCID() + ": [");
//						for (HostRequestedDiagnostic hrd : rp.getHostsList().values()){
//							System.out.print(hrd.getHostId() + "(" + hrd.isAnswered() +"),");
//						}
//						System.out.println("]");
//					}
					
					// Print U Sets
//					auxHost.comparatorModule.printUSets();
				}
				
				if (j > 0) {
					media += auxHost.downloadStats.sum();
					Logs.printMessage("main", "Host " + j + " recebeu " + auxHost.downloadStats.sum());
				}
			}
			
			Integer pacotesGerados= (((SourceHost) chainsaw.overlay.getHost(0)).seqNo);
			Logs.printConfig((double)pacotesGerados, (double) Simulator.getInstance().getIntTotalChunksReceived() / (hostsList.length - 1));
			
			Logs.printMessage("summary", Simulator.getInstance().getIntMessagesSimpleSimulation() + " messages were sent between peers to transmite data.");
			
			Logs.printMessage("summary", Simulator.getInstance().getIntTotalChunksSent() + " chunks were sent by peers to transmite data.");
			Logs.printMessage("summary", Simulator.getInstance().getIntTotalChunksReceived() + " chunks were received by peers.");
			
			if (StreamingConfig.SIMULATION_WITH_ATTACKERS){
				Logs.printMessage("summary", Simulator.getInstance().getIntOriginalChunksCounter() + " original chunks were received by peers.");
				Logs.printMessage("summary", Simulator.getInstance().getIntModifiedChunksCounter() + " modified chunks were sent by peers.");
			}
			
			if (Config.USE_COMPARATOR_MODULE){
				chainsaw.printPollutedPeers();
				
				Logs.printMessage("summary", Simulator.getInstance().getIntDiagnosticRequestMessages() + " request messages were transmited by comparator modules to do the system diagnostic.");
				Logs.printMessage("summary", Simulator.getInstance().getIntDiagnosticResponseMessages() + " response messages were transmited by comparator modules to do the system diagnostic.");
				Logs.printMessage("summary", (Simulator.getInstance().getIntDiagnosticRequestMessages() + Simulator.getInstance().getIntDiagnosticResponseMessages())  + " messages in total were transmited by comparator modules to do the system diagnostic.");
				Tracker.getInstance().printTSet();
			}
			
			String results = Simulator.getInstance().getIntTotalChunksSent() + ","; // number of chunks sent by peers
			results += Simulator.getInstance().getIntDiagnosticResponseMessages() + ","; // number of messages with chunks from CMs
			results += Simulator.getInstance().getIntDiagnosticRequestMessages() + ","; // number of request messages
			results += Simulator.getInstance().getIntTotalUSetsSentTracker() + ","; // number of U sets sent to tracker
			results += Simulator.getInstance().getIntModifiedChunksCounter() + ","; // number of peers that received polluted chunks
			results += chainsaw.meanPollutedInDiagnosed() + ","; // mean of polluted peers with cids diagnosed
			results += chainsaw.diagnosticAccuracy() + "%\n";
			
			FileLog.getInstance().appendStringResults(results);

			/* Coleta de estatísticas das simulações em arquivos */
			String attackerRatio = StreamingConfig.ATTACKERS_RATIO + "";
			
			// Adds a '0' on the string to make files better looking
			if (StreamingConfig.ATTACKERS_RATIO == 0.0 || StreamingConfig.ATTACKERS_RATIO == 0.1 || StreamingConfig.ATTACKERS_RATIO == 0.2) {
				attackerRatio += "0";
			}
			
			/* Creating some string variable to use in Final Stats log files... */
			// Check what kind of pollution was used
			String polluterType;
			
			if (StreamingConfig.ATTACK_TYPE == 3) {
				polluterType = "omite";
			}
			else {
				if (StreamingConfig.ALWAYS_POLLUTE) {
					polluterType = "sempre";
				}
				else {
					polluterType = "aleat";
				}
			}
			
			// Churn used 
			String churnUsed = "semch";
			
			if (StreamingConfig.DYNAMIC_INSERTION_ON){
				churnUsed = "comch" + 100 * StreamingConfig.HOSTS_NUMBER_TO_INSERT_ON_DISTRIBUTION / StreamingConfig.INITIAL_HOSTS_NUMBER;  
			}
			
/* 1 */			// - qtde de chunks enviados (reply) normalmente pelo Fireflies (sem o MC) --> DADOS_chunks-env-fireflies.txt 	(1 linha p/ cada simulacao, exemplo: "[5%,sempre,semch,200,15] #sim qtde" )
			FileLog.getInstance().saveFinalStatsInFile("DADOS_chunks-env-fireflies", 
					"["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " 
					+ numSim + " " + Simulator.getInstance().getIntTotalChunksSent() + "\n");
			
/* 2 */			// - qtde de chunks enviados especificamente pelo MC (reply de um request do MC) --> DADOS_chunks-env-mod-comp.txt 	(1 linha p/ cada simulacao, exemplo: [5%,sempre,semch,200,15] #sim qtde )
			FileLog.getInstance().saveFinalStatsInFile("DADOS_chunks-env-mod-comp", 
					"["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " 
					+ numSim + " " + Simulator.getInstance().getIntDiagnosticResponseMessages() + "\n");
			
			

/* 3 */			// - apenas para os chunks monitorados, quantos peers receberam o conteúdo diferente do original --> DADOS_peers-poluidos_CID.txt 	(1 linha p/ cada simulacao/cid, ex.: [5%,sempre,semch,200,15] #sim cid qtde )				
			String strMonitoredChunksPollutedPeers = new String();
			
/* 4 */			// - apenas para os chunks monitorados, quantos peers que receberam o conteúdo diferente do original, foram diagnosticados corretamente pelo tracker para o respectivo T_{cid}   --> DADOS_chunks-poluidos-diag_CID.txt	(1 linha p/ cada simulacao/cid, ex.: [5%,sempre,semch,200,15] #sim cid qtde_diag )
			String strTotalDiagnosed = new String();

/* 9 */			// - apenas para os chunks monitorados, quem sao os peers que foram incluidos no conjunto dos que não respondem --> DADOS_peers_sem-resp_CID.txt	(1 linha p/ cada simulacao/cid, ex.: [5%,sempre,semch,200,15] #sim cid qtde_peers lista_peers )				
			String strDiagnosedAsNotAnswered = new String();				
			String strPeersDiagnosedAsNotAnswered = new String();
			
/* 10 */		// - apenas para os chunks monitorados, quem sao os peers que foram configurados como omissores --> DADOS_peers_conf-omissores_CID.txt	(1 linha p/ cada simulacao/cid, ex.: [5%,sempre,semch,200,15] #sim cid qtde_peers lista_peers )
			Integer intNotAnsweredAndConfiguredAsOmiter = new Integer(0);
			String strNotAnsweredAndConfiguredAsOmiter = new String();
			String strPeersNotAnsweredAndConfiguredAsOmiter = new String();
			
/* 11 */		// - apenas para os chunks monitorados, quantos nao responderam (-) quantos foram configurados omissores --> DADOS_peers_timeout_CID.txt		(1 linha p/ cada simulacao/cid, ex.: [5%,sempre,semch,200,15] #sim cid qtde_peers)				
			Integer intDifDiagnosticNotAnswered = new Integer(0);
			String strDifDiagnosticNotAnswered = new String();
			
			// For each T set calculated by the Tracker ...
			for (TrackerRequest tr : Tracker.getInstance().getTSet().values()){
				Integer totalDiagnosed = 0;
				
				int intPollutedPeers = (Simulator.getInstance().getPollutedPeers().get(tr.getCid()) != null) ? Simulator.getInstance().getPollutedPeers().get(tr.getCid()).size() : 0;
				/* 3  Build string for each chunk monitored and the total polluted peers  */
				strMonitoredChunksPollutedPeers += "["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + 
					StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " + 
					numSim + " " + tr.getCid() + " " + intPollutedPeers + "\n";
				
				/* 9 Peers that were grouped in Not Answered */
				strPeersDiagnosedAsNotAnswered = "["; 
				
				/* 10 Peers grouped in Not Answered and who are omitter */
				strPeersNotAnsweredAndConfiguredAsOmiter = "[";
				
				intNotAnsweredAndConfiguredAsOmiter = 0;
				if (Tracker.getInstance().getNotAnsweredDiagnostic().get(tr.getCid()) != null){
					for (Integer hostID : Tracker.getInstance().getNotAnsweredDiagnostic().get(tr.getCid()).values()){
						strPeersDiagnosedAsNotAnswered += hostID + ",";
						
						// If host detected as not answered is an attacker3 (omitter)
						
						if (Overlay.getInstance().getHost(hostID) != null){
							if (Overlay.getInstance().getHost(hostID).getClass().getName().contains("Attacker3")){
								intNotAnsweredAndConfiguredAsOmiter++;
								strPeersNotAnsweredAndConfiguredAsOmiter += hostID + ",";
							}
						}
					}
				}
				
				strPeersDiagnosedAsNotAnswered += "]";
				strPeersNotAnsweredAndConfiguredAsOmiter += "]";
				
				/* 9 Build string for the number of peers that not answered the request */
				int intDiagnosedAsNotAnswered = (Tracker.getInstance().getNotAnsweredDiagnostic().get(tr.getCid()) != null) ? Tracker.getInstance().getNotAnsweredDiagnostic().get(tr.getCid()).size() : 0;
				
				strDiagnosedAsNotAnswered += "["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + 
				StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " + 
				numSim + " " + tr.getCid() + " " + intDiagnosedAsNotAnswered + " " + strPeersDiagnosedAsNotAnswered + "\n";
				
				
				/* 10 Build string for the peers that haven't answered and are configured as omiter */
				strNotAnsweredAndConfiguredAsOmiter += "["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + 
				StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " + 
				numSim + " " + tr.getCid() + " " + intNotAnsweredAndConfiguredAsOmiter + " " + strPeersNotAnsweredAndConfiguredAsOmiter + "\n";
				
				
				intDifDiagnosticNotAnswered = intDiagnosedAsNotAnswered - intNotAnsweredAndConfiguredAsOmiter;
				
				/* 11 Build string */
				strDifDiagnosticNotAnswered += "["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + 
				StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " + 
				numSim + " " + tr.getCid() + " " + intDifDiagnosticNotAnswered + "\n";
												
				/* 4 */
				// ... if any peers received the polluted chunk... 
				if (Simulator.getInstance().getPollutedPeers().get(tr.getCid()) != null){
					// ... check if each peer with polluted chunk was correctlly diagnosed
					for (Integer hostId : Simulator.getInstance().getPollutedPeers().get(tr.getCid()).values()){
						// If the peers was identified, increases the number of correct diagnostic
						if (Tracker.getInstance().getFaultDiagnostic().get(tr.getCid()) != null){
							if (Tracker.getInstance().getFaultDiagnostic().get(tr.getCid()).containsValue(hostId)){
								totalDiagnosed++;
							}
						}
					}
				}
				/* 4  Build string for each chunk monitored how many of 'em were diagnosed */
				strTotalDiagnosed += "["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + StreamingConfig.INITIAL_HOSTS_NUMBER + "," + 
					(int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " + numSim + " " + tr.getCid() + " " + totalDiagnosed + "\n";
			}
			
			FileLog.getInstance().saveFinalStatsInFile("DADOS_peers-poluidos_CID", strMonitoredChunksPollutedPeers); 	/* 3  save file*/
			FileLog.getInstance().saveFinalStatsInFile("DADOS_chunks-poluidos-diag_CID", strTotalDiagnosed);			/* 4  save file */
			FileLog.getInstance().saveFinalStatsInFile("DADOS_peers_sem-resp_CID", strDiagnosedAsNotAnswered); 			/* 9  save file*/
			FileLog.getInstance().saveFinalStatsInFile("DADOS_peers_conf-omissores_CID", strNotAnsweredAndConfiguredAsOmiter);	/* 10  save file*/
			FileLog.getInstance().saveFinalStatsInFile("DADOS_peers_timeout_CID", strDifDiagnosticNotAnswered);			/* 11  save file*/
			
/* 5 */		// - para TODOS os chunks, quantos peers receberam o conteúdo diferente do original --> DADOS_total_peers-poluidos.txt 	(1 linha p/ cada simulacao, ex.: "[5%,sempre,semch,200,15] #sim #qtde_media" )
			Double totalPolluted = 0.0;
			for (int cid = 0 ; cid <= (Config.TIME * StreamingConfig.MCASTRATE) ; cid++){
				/* 5  Build string for each chunk transmited how many of 'em received polluted data */
				totalPolluted += Simulator.getInstance().getPollutedPeers().get(cid) != null ? Simulator.getInstance().getPollutedPeers().get(cid).size() : 0;
			}
			
			// mean of polluted peers
			totalPolluted /= (Config.TIME * StreamingConfig.MCASTRATE);
			
			/* 5  save file */
			FileLog.getInstance().saveFinalStatsInFile("DADOS_total_peers-poluidos", 
					"["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " 
					+ numSim + " " + totalPolluted + "\n");
			
			String strChunksSentWithoutCMPerSecond = new String();
			String strChunksSentByCMPerSecond = new String();
			String strUSetsSentPerSecond = new String();
			
			for (int i = 0 ; i < (int)(Config.TIME + StreamingConfig.MCASTWINDOWSIZE); i++){
				/* 6 */			// - chunks/s enviados (reply) normalmente pelo Fireflies (sem o MC) --> DADOS_chunks-env-fireflies_SEG.txt	(1 linha p/ cada simulacao/seg, ex.: [5%,sempre,semch,200,15] #sim seg qtde_chunks )
				strChunksSentWithoutCMPerSecond += "["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + 
				StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " + 
				numSim + " " + i + " " + Simulator.getInstance().chunksSentWithoutCMPerSecond[i] + "\n";
				
				/* 7 */			// - chunks/s enviados especificamente pelo MC (reply de um request do MC) --> DADOS_chunks-env-mod-comp_SEG.txt	(1 linha p/ cada simulacao/seg, ex.: [5%,sempre,semch,200,15] #sim seg qtde_chunks )
				strChunksSentByCMPerSecond += "["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + 
				StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " + 
				numSim + " " + i + " " + Simulator.getInstance().chunksSentByCMPerSecond[i] + "\n";

				/* 8 */			// - envio/s de conjuntos U dos peers ao tracker --> DADOS_env-conj_U_SEG.txt		(1 linha p/ cada simulacao/seg, ex.: [5%,sempre,semch,200,15] #sim seg qtde_env )
				strUSetsSentPerSecond += "["+ attackerRatio + "," + polluterType + "," + churnUsed +"," + 
				StreamingConfig.INITIAL_HOSTS_NUMBER + "," + (int)StreamingConfig.TRACKER_CHUNK_SELECTION_INTERVAL + "] " + 
				numSim + " " + i + " " + Simulator.getInstance().uSetsSentPerSecond[i] + "\n";
			}
			
			FileLog.getInstance().saveFinalStatsInFile("DADOS_chunks-env-fireflies_SEG", strChunksSentWithoutCMPerSecond); 		/* 6  save file*/
			FileLog.getInstance().saveFinalStatsInFile("DADOS_chunks-env-mod-comp_SEG", strChunksSentByCMPerSecond);			/* 7  save file */
			FileLog.getInstance().saveFinalStatsInFile("DADOS_env-conj_U_SEG", strUSetsSentPerSecond); 							/* 8  save file*/
			
			chainsaw = null;
			Simulator simulator = Simulator.getInstance();
			Tracker.getInstance().resetTracker();
			Overlay.getInstance().resetOverlay();
			simulator.reset();
			Logs.printMessage("main", "-------------------- Fim execução " + (numSim+1) + " --------------------");
			Logs.printMessage("summary", "-------------------- Fim execução " + (numSim+1) + " --------------------");
		
		FileLog.getInstance().closeFileLog();
		FileLog.getInstance().closeFileResults();
		FileLog.getInstance().closeFileParameters();
		FileLog.nullInstance();
	}

	private static void setConfigurations(int i) {
		switch (i) {
		case 1:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.0;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 2:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.05;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 3:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.1;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 4:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.15;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 5:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.2;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 6:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.25;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 7:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.0;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 8:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.05;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 9:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.10;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 10:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.15;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 11:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.2;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 12:
			StreamingConfig.DYNAMIC_INSERTION_ON = false;
			StreamingConfig.DYNAMIC_REMOVAL_ON = false;
			StreamingConfig.ATTACKERS_RATIO = 0.25;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 13:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.0;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 14:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.05;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 15:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.10;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 16:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.15;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 17:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.20;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 18:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.25;
			StreamingConfig.ALWAYS_POLLUTE = true;
			break;
		case 19:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.0;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 20:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.05;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 21:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.10;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 22:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.15;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 23:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.20;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		case 24:
			StreamingConfig.DYNAMIC_INSERTION_ON = true;
			StreamingConfig.DYNAMIC_REMOVAL_ON = true;
			StreamingConfig.ATTACKERS_RATIO = 0.25;
			StreamingConfig.ALWAYS_POLLUTE = false;
			break;
		}
	}

	private void printPollutedPeers() {
		FileLog.getInstance().appendStringParameters("Polluted peers:\n\n");
		String pollutedPeers = "";
		for (TrackerRequest tr : Tracker.getInstance().getTSet().values()){
			pollutedPeers += "Chunk " + tr.getCid() + ": [";
			// for each polluted peer in cid
			if (Simulator.getInstance().getPollutedPeers().get(tr.getCid()) != null){
				for (Integer hostId : Simulator.getInstance().getPollutedPeers().get(tr.getCid()).values()){
					pollutedPeers += hostId + ",";
				}
				pollutedPeers += "]";
				FileLog.getInstance().appendStringParameters(pollutedPeers);
			}
		}
	}

	public double meanPollutedInDiagnosed(){
		Integer totalPolluted = 0;
		
		for (TrackerRequest tr : Tracker.getInstance().getTSet().values()){
			// for each polluted peer in cid
			if (Simulator.getInstance().getPollutedPeers().get(tr.getCid()) != null){
				totalPolluted += Simulator.getInstance().getPollutedPeers().get(tr.getCid()).size();
			}
		}
		
		return (totalPolluted / Tracker.getInstance().getChunksToBeCompared().size());
	}
	
	public double diagnosticAccuracy(){
		Integer totalPolluted = 0;
		Integer totalDiagnosed = 0;
		
		for (TrackerRequest tr : Tracker.getInstance().getTSet().values()){
			// for each polluted peer in cid
			if (Simulator.getInstance().getPollutedPeers().get(tr.getCid()) != null){
				for (Integer hostId : Simulator.getInstance().getPollutedPeers().get(tr.getCid()).values()){
					totalPolluted++;
					if (Tracker.getInstance().getFaultDiagnostic().get(tr.getCid()) != null){
						if (Tracker.getInstance().getFaultDiagnostic().get(tr.getCid()).containsValue(hostId)){
							totalDiagnosed++;
						}
					}
				}
			}
		}
		
		if (totalDiagnosed == 0){ //if no peers were diagnosed, avoid division by 0, returning 0
			return 0;
		}
		else {
			return (totalPolluted / totalDiagnosed);
		}
	}
	
	/**
	 * @brief Starts the experiment without attackers
	 */
	public void experimentWithoutAttackers() {
		Simulator simulator = Simulator.getInstance();
		TopologyGenerator.firefliesTopology(StreamingConfig.INITIAL_HOSTS_NUMBER, Config.RING_NUM);

		periodicStats.addEvent();
		((SourceHost) overlay.getHost(0)).propagate();
		simulator.run();
		
		Object[] hostsList = overlay.hosts.values().toArray();
		
		Arrays.sort(hostsList, new idComparator());
		
		int media = 0;
		for (int i = 0; i < hostsList.length; i++){
			Host auxHost = (Host) hostsList[i];
			
			// Check if the host has data not added to download and upload stats variables
			if (auxHost.receivedCount > 0){
				auxHost.downloadStats.add(auxHost.receivedCount);
			}
			
			if (auxHost.sentCount > 0){
				auxHost.uploadStats.add(auxHost.sentCount);
			}
			
			auxHost.printStats();
		}
		
		periodicStats.stop();
//		simulator.reset();
	}

	public void allAttacks() {
		for (double i = 0.05; i <= 0.25; i += 0.05) {
			System.out.println("Attack type 1 -- p = " + i);
			experimentWithAttackers(1, i);
		}
	}

	/**
	 * @brief Starts the experiment with attackers
	 * @param attackType One of the four kinds of attack
	 * @param ratioAttackers Ratio of hosts that will have a malicious behavior
	 */
	public void experimentWithAttackers(int attackType, double ratioAttackers) {

		Simulator simulator = Simulator.getInstance();
		
		int numberByz = (int) Math.round(ratioAttackers * StreamingConfig.INITIAL_HOSTS_NUMBER); // Number of peers with byzantine beahavior
		int numReps = 1; // Number of repetitions
		
		// Vector to the experiments data acquisition
		double minDownload[] = new double[numReps];
		double avgDownload[] = new double[numReps];
		double maxDownload[] = new double[numReps];
		double minUpload[] = new double[numReps];
		double avgUpload[] = new double[numReps];
		double maxUpload[] = new double[numReps];
		int goodNodesLost = 0;

		for (int j = 0; j < numReps; j++) {
			TopologyGenerator.firefliesByzTopology(StreamingConfig.INITIAL_HOSTS_NUMBER, Config.RING_NUM, attackType,
					ratioAttackers);
			periodicStats.addEvent();
			((SourceHost) overlay.getHost(0)).propagate();
			
			simulator.run();
			
			/* Do the measures based on data acquired from simulation */
			Host host;

			Statistics downloadStats = new FullStatistics();
			Statistics uploadStats = new FullStatistics();
			for (int i = 1; i < (StreamingConfig.INITIAL_HOSTS_NUMBER - numberByz); i++) {
				host = overlay.getHost(i);
				if (host != null) {
					downloadStats.add(host.downloadStats.avg());
					uploadStats.add(host.uploadStats.avg());
				} else {
					goodNodesLost++;
				}
			}

			avgDownload[j] = downloadStats.avg();
			minDownload[j] = downloadStats.min();
			maxDownload[j] = downloadStats.max();
			avgUpload[j] = uploadStats.avg();
			minUpload[j] = uploadStats.min();
			maxUpload[j] = uploadStats.max();

			periodicStats.stop();

//			simulator.reset();
		}
		double r = StreamingConfig.MCASTRATE;

		try {
			PrintStream out = new PrintStream(new FileOutputStream(filename
					+ "Down.rates"));
			for (int i = 0; i < avgDownload.length; i++) {
				out.println(avgDownload[i] / r + " " + minDownload[i] / r + " "
						+ maxDownload[i] / r);
			}
			out.close();
			out = new PrintStream(new FileOutputStream(filename + "Up.rates"));
			for (int i = 0; i < avgDownload.length; i++) {
				out.println(avgUpload[i] / r + " " + minUpload[i] / r + " "
						+ maxUpload[i] / r);
			}
			out.close();
		} catch (Exception e) {
			System.out.println("Problems writing output to file");
		}
		Arrays.sort(avgDownload);
		Arrays.sort(minDownload);
		Arrays.sort(maxDownload);
		Arrays.sort(avgUpload);
		Arrays.sort(minUpload);
		Arrays.sort(maxUpload);

		int index5 = (int) (0.05 * avgDownload.length) - 1;
		int median = (int) (0.5 * avgDownload.length) - 1;
		int index95 = (int) (0.95 * avgDownload.length) - 1;
		
//		System.out.println(" hello " + index5 + " " + median + " " + index95);
		
		if (index5 == -1)
			index5 = 0;
		if (median == -1)
			median = 0;
		if (index95 == -1)
			index95 = 0;
		
		System.out.println("Minimum Download: ");
		System.out.println("5% - " + minDownload[index5] / r + " | " +
				"Median - " + minDownload[median] / r + " | " + 
				"95% - " + minDownload[index95] / r + "\n");
		
		System.out.println("Average Download: ");
		System.out.println("5% - " + avgDownload[index5] / r + " | " +
				"Median - " + avgDownload[median] / r + " | " +
				"95% - " + avgDownload[index95] / r + "\n");
		
		System.out.println("Maximum Download: ");
		System.out.println("5% - " + maxDownload[index5] / r + " | " +
				"Median - " + maxDownload[median] / r + " | " +
				"95% - " + maxDownload[index95] / r + "\n");
		
		
		System.out.println("Minimum Upload: ");
		System.out.println("5% - " + minUpload[index5] / r + " | " +
				"Median - " + minUpload[median] / r + " | " + 
				"95% - " + minUpload[index95] / r + "\n");
		
		System.out.println("Average Upload: ");
		System.out.println("5% - " + avgUpload[index5] / r + " | " + 
				"Median - " + avgUpload[median] / r + " | " + 
				"95% - " + avgUpload[index95] / r + "\n");
		
		System.out.println("Maximum Upload: ");
		System.out.println("5% - " + maxUpload[index5] / r + " | " + 
				"Median - " + maxUpload[median] / r + " | " + 
				"95% - " + maxUpload[index95] / r + "\n");
	}
	public class idComparator implements Comparator{
		
		public int compare(Object arg0, Object arg1) {
			Host h1 = (Host) arg0;
			Host h2 = (Host) arg1;
			
			if (h1.getId() > h2.getId()){
				return 1;
			}
			else if (h1.getId() > h2.getId()){
				return -1;
			}
			else {
				return 0;
			}
		}
		
	}
}
